package org.raymond.iworks.study.netty.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * @author raymond
 * @version V1.0
 * @Description: TODO
 */
public class NIOServer {

    public static void main(String[] args) throws Exception {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 12000);
        // 绑定地址
        ssc.bind(address);
        // 设置为非阻塞
        ssc.configureBlocking(false);
        // 打开一个selector
        Selector selector = Selector.open();
        // 将一个serverSocketChannel注册到一个selector中,监听接受连接事件
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        // 等待客户端发起连接
        while(true){
            // 等待3秒.(返回0相当于没有事件),如果没有事件,则跳过
            if(selector.select(3000)==0){
                System.out.println("服务器等待了3秒,没有等到连接");
                continue;
            }
            // 如果有事件,即selector.select(3000)>0,则获取事件
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            // 获取迭代器遍历
            Iterator<SelectionKey> it = selectedKeys.iterator();
            while(it.hasNext()){
                // 获取到事件
                SelectionKey selectionKey = it.next();
                // 如果是连接事件
                if(selectionKey.isAcceptable()){
                    // 服务器与客户端建立连接, 获取socketChannel
                    ServerSocketChannel sscByKey = (ServerSocketChannel)selectionKey.channel();
                    // 非阻塞模式下, accept不会等到有连接才返回,而是直接返回,因此有可能返回null
                    SocketChannel sc = sscByKey.accept(); // it.remove之后,不会再收到空对象
                    // 设置成非阻塞
                    sc.configureBlocking(false);
                    // 将一个socketChannel注册到selector中,监听读事件,并绑定一个缓冲区
                    sc.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }
                // 如果是读事件
                if(selectionKey.isReadable()){
                    // 获取Socket通道
                    SocketChannel sc = (SocketChannel)selectionKey.channel();
                    // 获取关联的buffer
                    ByteBuffer bf = (ByteBuffer)selectionKey.attachment();
                    // 打印从客户端获取到的数据
                    int read = sc.read(bf);
                    // 接收到消息
                    if(read>0) {
                        System.out.println("from 客户端:" + new String(bf.array()).trim()); // 利用trim去掉后面的空格
                        // 如果客户端断开连接
                    }else if(read==-1) {
                        sc.close();
                    }
                }
                // 从事件集合中删除本次处理的key,防止下次重复处理
                it.remove();
            }
        }
    }
}
